Don't panic while streaming compiler output
authorAleksey Kladov <aleksey.kladov@gmail.com>
Tue, 27 Sep 2016 11:39:08 +0000 (14:39 +0300)
committerAleksey Kladov <aleksey.kladov@gmail.com>
Sun, 2 Oct 2016 14:28:26 +0000 (17:28 +0300)
src/cargo/ops/cargo_rustc/custom_build.rs
src/cargo/ops/cargo_rustc/mod.rs
src/cargo/util/errors.rs
src/cargo/util/process_builder.rs

index 8835824898c5aafcac90fa5fbcf972ff21636e8f..5a15e85a853af264e18350ffc4e99cd78e95f31e 100644 (file)
@@ -206,8 +206,8 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>)
         state.running(&p);
         let cmd = p.into_process_builder();
         let output = try!(cmd.exec_with_streaming(
-            &mut |out_line| state.stdout(out_line),
-            &mut |err_line| state.stderr(err_line),
+            &mut |out_line| { state.stdout(out_line); Ok(()) },
+            &mut |err_line| { state.stderr(err_line); Ok(()) },
         ).map_err(|mut e| {
             e.desc = format!("failed to run custom build command for `{}`\n{}",
                              pkg_name, e.desc);
index dac09a7ea38ef7a4f58db573efa9d7ee7ecbfd01..7d423dd8dcb3f957e4cc5de9db28c152a2319a05 100644 (file)
@@ -280,11 +280,15 @@ fn rustc(cx: &mut Context, unit: &Unit) -> CargoResult<Work> {
                 message: json::Json,
             }
             process_builder.exec_with_streaming(
-                &mut |line| assert!(line.is_empty()),
+                &mut |line| if !line.is_empty() {
+                    Err(internal(&format!("compiler stdout is not empty: `{}`", line)))
+                } else {
+                    Ok(())
+                },
                 &mut |line| {
-                    let rustc_message = json::Json::from_str(line).unwrap_or_else(|_| {
-                        panic!("Compiler produced invalid json: `{}`", line)
-                    });
+                    let rustc_message = try!(json::Json::from_str(line).map_err(|_| {
+                        internal(&format!("compiler produced invalid json: `{}`", line))
+                    }));
 
                     let message = Message {
                         reason: "rustc-message",
@@ -294,7 +298,7 @@ fn rustc(cx: &mut Context, unit: &Unit) -> CargoResult<Work> {
                     };
                     let encoded = json::encode(&message).unwrap();
                     println!("{}", encoded);
-
+                    Ok(())
                 },
             ).map(|_| ())
         } else {
index 8b41dd3e6ce4205e3cff26b3e69852b8d1e09249..39e22cbe5da45d7357d6fad0500549b916723847 100644 (file)
@@ -110,13 +110,13 @@ pub struct ProcessError {
     pub desc: String,
     pub exit: Option<ExitStatus>,
     pub output: Option<Output>,
-    cause: Option<io::Error>,
+    cause: Option<Box<Error + Send>>,
 }
 
 impl Error for ProcessError {
     fn description(&self) -> &str { &self.desc }
     fn cause(&self) -> Option<&Error> {
-        self.cause.as_ref().map(|s| s as &Error)
+        self.cause.as_ref().map(|e| &**e as &Error)
     }
 }
 
@@ -375,9 +375,10 @@ impl CargoError for str::ParseBoolError {}
 // Construction helpers
 
 pub fn process_error(msg: &str,
-                     cause: Option<io::Error>,
+                     cause: Option<Box<Error + Send>>,
                      status: Option<&ExitStatus>,
-                     output: Option<&Output>) -> ProcessError {
+                     output: Option<&Output>) -> ProcessError
+{
     let exit = match status {
         Some(s) => status_to_string(s),
         None => "never executed".to_string(),
index 856e337e200de514e98af0c7d12ecf7c13ee54f9..c0d88d2ae8603b81d523a01dd3cd44d8efe9c179 100644 (file)
@@ -5,7 +5,7 @@ use std::fmt;
 use std::path::Path;
 use std::process::{Command, Stdio, Output};
 
-use util::{ProcessError, process_error, read2};
+use util::{CargoResult, ProcessError, process_error, read2};
 use util::shell_escape::escape;
 
 #[derive(Clone, PartialEq, Debug)]
@@ -77,7 +77,7 @@ impl ProcessBuilder {
         let exit = try!(command.status().map_err(|e| {
             process_error(&format!("could not execute process `{}`",
                                    self.debug_string()),
-                          Some(e), None, None)
+                          Some(Box::new(e)), None, None)
         }));
 
         if exit.success() {
@@ -94,8 +94,8 @@ impl ProcessBuilder {
 
         let output = try!(command.output().map_err(|e| {
             process_error(&format!("could not execute process `{}`",
-                               self.debug_string()),
-                          Some(e), None, None)
+                                   self.debug_string()),
+                          Some(Box::new(e)), None, None)
         }));
 
         if output.status.success() {
@@ -108,8 +108,8 @@ impl ProcessBuilder {
     }
 
     pub fn exec_with_streaming(&self,
-                               on_stdout_line: &mut FnMut(&str),
-                               on_stderr_line: &mut FnMut(&str))
+                               on_stdout_line: &mut FnMut(&str) -> CargoResult<()>,
+                               on_stderr_line: &mut FnMut(&str) -> CargoResult<()>)
                                -> Result<Output, ProcessError> {
         let mut stdout = Vec::new();
         let mut stderr = Vec::new();
@@ -119,6 +119,7 @@ impl ProcessBuilder {
             .stderr(Stdio::piped())
             .stdin(Stdio::null());
 
+        let mut callback_error = None;
         let status = try!((|| {
             let mut child = try!(cmd.spawn());
             let out = child.stdout.take().unwrap();
@@ -137,10 +138,14 @@ impl ProcessBuilder {
                 let start = dst.len();
                 dst.extend(data);
                 for line in String::from_utf8_lossy(&dst[start..]).lines() {
-                    if is_out {
+                    if callback_error.is_some() { break }
+                    let callback_result = if is_out {
                         on_stdout_line(line)
                     } else {
                         on_stderr_line(line)
+                    };
+                    if let Err(e) = callback_result {
+                        callback_error = Some(e);
                     }
                 }
             }));
@@ -148,7 +153,7 @@ impl ProcessBuilder {
         })().map_err(|e| {
             process_error(&format!("could not execute process `{}`",
                                    self.debug_string()),
-                          Some(e), None, None)
+                          Some(Box::new(e)), None, None)
         }));
         let output = Output {
             stdout: stdout,
@@ -159,6 +164,10 @@ impl ProcessBuilder {
             Err(process_error(&format!("process didn't exit successfully: `{}`",
                                        self.debug_string()),
                               None, Some(&output.status), Some(&output)))
+        } else if let Some(e) = callback_error {
+            Err(process_error(&format!("failed to parse process output: `{}`",
+                                       self.debug_string()),
+                              Some(Box::new(e)), Some(&output.status), Some(&output)))
         } else {
             Ok(output)
         }